package com.fucking.great.context.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.Session;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.db.sql.Condition;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.setting.Setting;
import cn.hutool.system.SystemUtil;
import com.fucking.great.context.TxnContext;
import com.fucking.great.context.constant.EnvConstant;
import com.fucking.great.context.plun.Plug;
import com.fucking.great.context.plun.PlugFactory;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 再数据库中获得配置项
 * init : select value from great_configure_data where system_name = ?   and status = 1;
 * getservicescript :  select script from great_script_init where path = ?   and status = 1;
 * getresultscript :  select value from great_configure_data where system_name = ? and key = 'viewresult'  and status = 1;
 */
public class DBTxnConfingerImpl extends BaseTxnConfiger {
    private static final Log log = LogFactory.get();
    private static Db use;
    // init Db use;
    static {
        final Setting setting = new Setting();
        // 注册组件。
        setting.put("", "HTTP,DB,CONTEXT,callService");
        // 防止初始化失败。

        // mysql_password
        String username = SystemUtil.get(EnvConstant.DB_USERNAME_KEY, "root");
        setting.put("username", username);

        //
        String password = SystemUtil.get(EnvConstant.DB_PASSWORD_KEY, "root");
        setting.put("password", password);
        //
        String ip = SystemUtil.get(EnvConstant.DB_IP_KEY, "127.0.0.1");
        String port = SystemUtil.get(EnvConstant.DB_PORT_KEY, "3306");
        String url = "jdbc:mysql://" + ip + ":" + port + "/great?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&nullCatalogMeansCurrent=true";
        setting.put("url", url);
        String driver = "com.mysql.cj.jdbc.Driver";
        setting.put("driver", driver);
        // 连接池配置项
        setting.put("initialSize", "1");
        setting.put("maxActive", "8");
        setting.put("minIdle", "0");
        setting.put("maxWait", "0");
        setting.put("poolPreparedStatements", "false");
        setting.put("maxOpenPreparedStatements", "-1");
        setting.put("validationQuery", "SELECT 1");
        setting.put("testOnBorrow", "true");
        setting.put("testOnReturn", "false");
        setting.put("testWhileIdle", "true");
        setting.put("timeBetweenEvictionRunsMillis", "60000");
        setting.put("connectionInitSqls", "SELECT 1");
        setting.put("filters", "stat");
        setting.put("proxyFilters", "");
        /*
## 可选配置
# 是否在日志中显示执行的SQL
showSql = true
# 是否格式化显示的SQL
formatSql = false
# 是否显示SQL参数
showParams = true
# 打印SQL的日志等级，默认debug
sqlLevel = debug


## db.setting文件

url = jdbc:mysql://localhost:3306/test
user = root
pass = 123456

## 可选配置
# 是否在日志中显示执行的SQL
showSql = true
# 是否格式化显示的SQL
formatSql = false
# 是否显示SQL参数
showParams = true
# 打印SQL的日志等级，默认debug，可以是info、warn、error
sqlLevel = debug

#----------------------------------------------------------------------------------------------------------------
## 连接池配置项

## ---------------------------------------------------- Druid
# 初始化时建立物理连接的个数。初始化发生在显示调用init方法，或者第一次getConnection时
initialSize = 1
# 最大连接池数量
maxActive = 8
# 最小连接池数量
minIdle = 0
# 获取连接时最大等待时间，单位毫秒。配置了maxWait之后， 缺省启用公平锁，并发效率会有所下降， 如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
maxWait = 0
# 是否缓存preparedStatement，也就是PSCache。 PSCache对支持游标的数据库性能提升巨大，比如说oracle。 在mysql5.5以下的版本中没有PSCache功能，建议关闭掉。作者在5.5版本中使用PSCache，通过监控界面发现PSCache有缓存命中率记录， 该应该是支持PSCache。
poolPreparedStatements = false
# 要启用PSCache，必须配置大于0，当大于0时， poolPreparedStatements自动触发修改为true。 在Druid中，不会存在Oracle下PSCache占用内存过多的问题， 可以把这个数值配置大一些，比如说100
maxOpenPreparedStatements = -1
# 用来检测连接是否有效的sql，要求是一个查询语句。 如果validationQuery为null，testOnBorrow、testOnReturn、 testWhileIdle都不会其作用。
validationQuery = SELECT 1
# 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。
testOnBorrow = true
# 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能
testOnReturn = false
# 建议配置为true，不影响性能，并且保证安全性。 申请连接的时候检测，如果空闲时间大于 timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。
testWhileIdle = false
# 有两个含义： 1) Destroy线程会检测连接的间隔时间 2) testWhileIdle的判断依据，详细看testWhileIdle属性的说明
timeBetweenEvictionRunsMillis = 60000
# 物理连接初始化的时候执行的sql
connectionInitSqls = SELECT 1
# 属性类型是字符串，通过别名的方式配置扩展插件， 常用的插件有： 监控统计用的filter:stat  日志用的filter:log4j 防御sql注入的filter:wall
# filters = stat
# 类型是List<com.alibaba.druid.filter.Filter>， 如果同时配置了filters和proxyFilters， 是组合关系，并非替换关系
# proxyFilters =
         */
        String sqllevel = SystemUtil.get(EnvConstant.DB_SQLLEVEL_KEY);
        if (!TxnContext.IS_SERVICE || StrUtil.isNotBlank(sqllevel) ) {
            sqllevel = StrUtil.isBlank(sqllevel) ? "debug" : sqllevel;
            setting.put("showSql", "true");
            setting.put("formatSql", "false");
            setting.put("showParams", "true");
            setting.put("sqlLevel", sqllevel);
        }

        //默认setting文件数据源  ==> 远程数据库连接。
//        use = getSession(setting);
        //
        log.info("DBTxnConfinger-setting-config=[{}]",setting);
        DSFactory dsFactory = DSFactory.create(setting);
        DataSource dataSource = dsFactory.getDataSource();

        use = Db.use(dataSource);
    }


    @Override
    public PathConfigBean getPathConfigBean(String path) {
        //
        PathConfigBean pathConfigBean = new PathConfigBean();
        pathConfigBean.setPath(path);
        //
        try {
            Entity entity = Entity.create("great_script_init").set("status", 1).set("path", path);
            // 设置查到的字段
            entity.setFieldNames("script","plungs","importPackage");
            Entity data = use.get(entity);
            //
            String script = data.getStr("script");
            String plungs = data.getStr("plungs");
            String importPackage = data.getStr("importPackage");
            //
            if (StrUtil.isBlank(plungs)) {
                // 取全局的.
                String systemName = TxnContext.getContext().getSystemName();
                plungs = getSystemConfigBean(systemName).getConfig().remove("plungs");
                // 可能全局里面也没配置
                plungs = plungs == null ? "" : plungs;
            }
            //
            pathConfigBean.setServiceScript(script);
            pathConfigBean.setPluns( StrUtil.splitToArray(plungs,',') );
            pathConfigBean.setImportPackage(StrUtil.splitToArray(importPackage,','));
        } catch (SQLException throwables) {
            log.error(throwables);
        }
        return pathConfigBean;
    }

    @Override
    public SystemConfigBean getSystemConfigBean(String systemName) {
        SystemConfigBean systemConfigBean = new SystemConfigBean();
        systemConfigBean.setSystemName(systemName);
        try {
            //

            Entity entity = Entity.create("great_configure_data").set("status", 1).set("system_name", systemName);
            // 设置查到的字段
            entity.setFieldNames("key","value");

            List<Entity> by = use.find(entity);
            //
            Map<String, String> config = IterUtil.toMap(by, k -> k.getStr("key"), v -> v.getStr("value"));
            //
            systemConfigBean.setResultScript(config.remove("viewResult"));
            systemConfigBean.setConfig(config);
            //
        } catch (SQLException throwables) {
            log.debug(throwables);
        }

        return systemConfigBean;
    }

    public String[] getAllSystemName(){
        String[] strings = null;

        try {
            List<Entity> query = null;
            query = use.query("select system_name from great_configure_data where status =1 group by system_name");
            int size = query.size();
            strings = new String[size];
            for (int i = 0; i < size; i++) {
                Entity entity = query.get(i);
                strings[i] = entity.getStr("system_name");
            }
        } catch (SQLException throwables) {
            log.debug(throwables);
        }

        return strings;
    }


}
